// created by noellynch
// Jun 16, 2009

package com.sceneworker.monitorpopup.panels;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.concurrent.Callable;

import javax.imageio.ImageIO;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import net.miginfocom.swing.MigLayout;

import com.jme.image.Texture;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.util.GameTaskQueueManager;
import com.myjavautils.swing.DrawUtils;
import com.myjavautils.swing.MigLayoutUtils;
import com.myjavautils.swing.SwingComponentFactory;
import com.sceneworker.app.events.DelegateEventTypes;
import com.sceneworker.util.delegation.DelegationEvent;
import com.sceneworker.util.delegation.DelegationEventBroadcaster;
import com.sceneworker.util.delegation.DelegationEventListener;

public class ViewMeshBuffersPanel extends JPanel implements DelegationEventListener {

    private     TriMesh                 mcl_mesh;
    private     TextureImagePanel       mcl_textureImage;
    
    private     JList                   mcl_triList;
    
    public  ViewMeshBuffersPanel(TriMesh cl_mesh, final JDialog cl_dlg) {
        mcl_mesh = cl_mesh;
        
        DelegationEventBroadcaster.inst().addEventListener(DelegateEventTypes.NEW_TRI_SELECTION_EVT, this);
        
        setLayout(new MigLayout("inset 5"));
        
        initialiseTextureCombo();
        initialiseTextureCoordsCombo();
        
        JCheckBox       lcl_cBox = SwingComponentFactory.makeACheckBox("Coordinates Only", false);
        lcl_cBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                mcl_textureImage.mb_coordsOnly = !mcl_textureImage.mb_coordsOnly;
                repaint();
            }
        });
        add(lcl_cBox, "wrap");
        
        initialiseTriangleList();
        
        mcl_textureImage = new TextureImagePanel();
        add(mcl_textureImage, "width 70%, height 80%, wrap");
        
        final   DelegationEventListener     lcl_this = this;
        
        JButton     lcl_done = SwingComponentFactory.makeAButton("Done");
        lcl_done.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                DelegationEventBroadcaster.inst().broadcast(new DelegationEvent(DelegateEventTypes.NO_TRI_SELECTION_EVT, this));
                DelegationEventBroadcaster.inst().removeEventListener(lcl_this);
                cl_dlg.setVisible(false);
            }
            
        });
        
        MigLayoutUtils.makeRightAlignedButtonBar(this, lcl_done);
        
        setNewTextureImage(0);
        mcl_textureImage.setTextureCoordinateListIndex(0);
    }
    
    public void handleEvent(DelegationEvent cl_e) {
        if(cl_e.getEventSource() != this) {
            if(cl_e.getEventType().equals(DelegateEventTypes.NEW_TRI_SELECTION_EVT)) {
                int     li_index = (Integer)cl_e.getObjectData()[1];
                mcl_triList.setSelectedIndex(li_index);
                mcl_triList.ensureIndexIsVisible(li_index);
                
                repaint();
            }
        }
    }

    private void    initialiseTriangleList() {
        JPanel      lcl_p = MigLayoutUtils.makeStandardPanel();
        MigLayoutUtils.makeLabelAndSeperator(lcl_p, "Triangles");
        
        mcl_triList = SwingComponentFactory.makeAList();
        mcl_triList.setModel(populateTriangleList());
        mcl_triList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        JScrollPane     lcl_scroller = new JScrollPane();
        lcl_scroller.setMinimumSize(new Dimension(200, 300));
        lcl_scroller.setViewportView(mcl_triList);
        lcl_p.add(lcl_scroller, "width 100%, wrap");
        
        final   JLabel      lcl_pt0 = SwingComponentFactory.makeALabel("v 0");
        final   JLabel      lcl_pt1 = SwingComponentFactory.makeALabel("v 1");
        final   JLabel      lcl_pt2 = SwingComponentFactory.makeALabel("v 2");
        
        mcl_triList.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                GameTaskQueueManager.getManager().update(new Callable<Void>() {
                    public Void call() throws Exception {
                        mcl_textureImage.setCurTriSel(mcl_triList.getSelectedIndex());
                        
                        int     li_indexBufferOffset = mcl_triList.getSelectedIndex() * 3;
                        IntBuffer       lcl_indexBuffer = mcl_mesh.getIndexBuffer();
                        lcl_indexBuffer.rewind();
                        FloatBuffer     lcl_vBuffer = mcl_mesh.getVertexBuffer();
                        
                        int     li_vOffset = lcl_indexBuffer.get(li_indexBufferOffset) * 3;
                        float   lf_temp = lcl_vBuffer.get(li_vOffset);
                        float   lf_temp2 = lcl_vBuffer.get(li_vOffset + 1);
                        float   lf_temp3 = lcl_vBuffer.get(li_vOffset + 2);
                        lcl_pt0.setText("v 0 = " + lf_temp +", " + lf_temp2 + ", " + lf_temp3);
                        
                        li_vOffset = lcl_indexBuffer.get(li_indexBufferOffset + 1) * 3;
                        lf_temp = lcl_vBuffer.get(li_vOffset);
                        lf_temp2 = lcl_vBuffer.get(li_vOffset + 1);
                        lf_temp3 = lcl_vBuffer.get(li_vOffset + 2);
                        lcl_pt1.setText("v 1 = " + lf_temp +", " + lf_temp2 + ", " + lf_temp3);
                        
                        li_vOffset = lcl_indexBuffer.get(li_indexBufferOffset + 2) * 3;
                        lf_temp = lcl_vBuffer.get(li_vOffset);
                        lf_temp2 = lcl_vBuffer.get(li_vOffset + 1);
                        lf_temp3 = lcl_vBuffer.get(li_vOffset + 2);
                        lcl_pt2.setText("v 2 = " + lf_temp +", " + lf_temp2 + ", " + lf_temp3);
                        
                        return null;
                    }
                });
            }
        });
        
        lcl_p.add(lcl_pt0, "span, wrap");
        lcl_p.add(lcl_pt1, "span, wrap");
        lcl_p.add(lcl_pt2, "span, wrap");
        
        add(lcl_p, "width 30%");
    }
    
    private DefaultListModel    populateTriangleList() {
        DefaultListModel        lcl_triModel = new DefaultListModel();
        
        IntBuffer          lcl_indexBuffer = mcl_mesh.getIndexBuffer();
        if(lcl_indexBuffer == null) {
            return lcl_triModel;
        }
        
        lcl_indexBuffer.rewind();
        for(int li_index = 0; li_index < lcl_indexBuffer.capacity(); li_index += 3) {
            lcl_triModel.addElement("(tri " + (li_index / 3) + ") "+ lcl_indexBuffer.get() + " " + lcl_indexBuffer.get() + " " + lcl_indexBuffer.get());
        }
        
        return lcl_triModel;
    }
    
    private void    initialiseTextureCoordsCombo() {
        ArrayList<TexCoords>        lcla_coords = mcl_mesh.getTextureCoords();
        
        if(lcla_coords.size() == 0) { return; }
        
        DefaultComboBoxModel        lcl_comboModel = new DefaultComboBoxModel();
        int     li_numViableCoords = 0;
        for(int li_index = 0; li_index < lcla_coords.size(); li_index++) {
            TexCoords       lcl_coords = lcla_coords.get(li_index);
            
            if(lcl_coords == null) {
                lcl_comboModel.addElement("Coords NULL");
            } else {
                li_numViableCoords++;
                lcl_comboModel.addElement("Coords " + li_index);
            }
        }
        
        if(li_numViableCoords == 0) {
            return;
        }
        
        final   JComboBox       lcl_combo = SwingComponentFactory.makeAComboBox();
        lcl_combo.setModel(lcl_comboModel);
        lcl_combo.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                mcl_textureImage.setTextureCoordinateListIndex(lcl_combo.getSelectedIndex());
            }
            
        });
        
        MigLayoutUtils.makeLabelAndSeperator(this, "Texture Coordinate List");
        add(lcl_combo, "width 50%, wrap");
    }
    
    private void    initialiseTextureCombo() {
        TextureState        lcl_ts = (TextureState)mcl_mesh.states[RenderState.StateType.Texture.ordinal()];
        
        if(lcl_ts != null) {
            if(lcl_ts.getNumberOfSetTextures() == 0) {
                return;
            }
            
            DefaultComboBoxModel        lcl_comboModel = new DefaultComboBoxModel();
            for(int li_index = 0; li_index < lcl_ts.getNumberOfSetTextures(); li_index++) {
                lcl_comboModel.addElement("Unit " + li_index);
            }
            
            final   JComboBox       lcl_combo = SwingComponentFactory.makeAComboBox();
            lcl_combo.setModel(lcl_comboModel);
            lcl_combo.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
                    int     li_tu = lcl_combo.getSelectedIndex();
                    setNewTextureImage(li_tu);
                }
            });
            
            
            MigLayoutUtils.makeLabelAndSeperator(this, "Texture Unit");
            add(lcl_combo, "width 50%, wrap");
        }
    }
    
    private void    setNewTextureImage(int i_unit) {
        TextureState        lcl_ts = (TextureState)mcl_mesh.states[RenderState.StateType.Texture.ordinal()];
        Texture     lcl_texture = lcl_ts.getTexture(i_unit);
        
        if(lcl_texture == null) {
            return;
        }
        
        if(lcl_texture.getImageLocation() != null) {
            try {
                mcl_textureImage.setTextureImage(new URL(lcl_texture.getImageLocation()));
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    class   TextureImagePanel extends JComponent {

        private     Dimension               mcl_curDim = new Dimension();
        private     BufferedImage           mcl_image;
        private     ArrayList<TempTri>      mcla_tris = new ArrayList<TempTri>();
        private     ArrayList<Polygon>        mcla_2dTris = new ArrayList<Polygon>();
        
        private     boolean         mb_coordsOnly = false;
        private     int             mi_curTriSel = -1;
        
        public  TextureImagePanel() {
            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    selectTri(e.getPoint());
                }
                
            });
        }
        
        private void    selectTri(Point cl_pt) {
            for(int li_index = 0; li_index < mcla_2dTris.size(); li_index++) {
                Polygon     lcl_p = mcla_2dTris.get(li_index);
                
                if(lcl_p.contains(cl_pt)) {
                    mi_curTriSel = li_index;
                    mcl_triList.setSelectedIndex(li_index);
                    mcl_triList.ensureIndexIsVisible(li_index);
                    mcl_triList.repaint();
                    
                    DelegationEventBroadcaster.inst().broadcast(new DelegationEvent(DelegateEventTypes.NEW_TRI_SELECTION_EVT, this, mcl_mesh, mi_curTriSel, false));
                    
                    repaint();
                }
            }
        }
        
        public  void    setTextureImage(URL str_im) {
            try {
                mcl_image = ImageIO.read(str_im);
            } catch (IOException e) {
                mcl_image = null;
            }
            
            repaint();
        }
        
        private void    build2DTris() {
            mcl_curDim.width = getWidth();
            mcl_curDim.height = getHeight();
            mcla_2dTris.clear();
            
            float   lf_w = (float)(getWidth() - 1);
            float   lf_h = (float)(getHeight() - 1);
            
            for(TempTri lcl_t : mcla_tris) {
                Polygon     lcl_poly = new Polygon();
                lcl_poly.addPoint((int)(lf_w * lcl_t.mfa_0[0]), (int)(lf_h * lcl_t.mfa_0[1]));
                lcl_poly.addPoint((int)(lf_w * lcl_t.mfa_1[0]), (int)(lf_h * lcl_t.mfa_1[1]));
                lcl_poly.addPoint((int)(lf_w * lcl_t.mfa_2[0]), (int)(lf_h * lcl_t.mfa_2[1]));
                                
                mcla_2dTris.add(lcl_poly);
            }
        }
        
        public  void    setCurTriSel(int i_sel) {
            if(i_sel != mi_curTriSel) {
                mi_curTriSel = i_sel;
                
                DelegationEventBroadcaster.inst().broadcast(new DelegationEvent(DelegateEventTypes.NEW_TRI_SELECTION_EVT, this, mcl_mesh, mi_curTriSel, false));
                
                repaint();
            }
        }
        
        public  void    setTextureCoordinateListIndex(int i_index) {
            TexCoords       lcl_coords = mcl_mesh.getTextureCoords(i_index);
            if(lcl_coords == null) {
                repaint();
                return;
            }
            
            if(lcl_coords.coords == null) {
                repaint();
                return;
            }
            
            lcl_coords.coords.rewind();
            mcla_tris.clear();
            
            IntBuffer       lcla_indexBuffer = mcl_mesh.getIndexBuffer();
            lcla_indexBuffer.rewind();
            
            for(int li_indexBufferIndex = 0; li_indexBufferIndex < lcla_indexBuffer.capacity(); li_indexBufferIndex += 3) {
                int     li_temp = lcla_indexBuffer.get(li_indexBufferIndex) * lcl_coords.perVert;
                
                TempTri     lcl_t = new TempTri(lcl_coords.perVert);
                
                for(int li_vI = 0; li_vI < lcl_coords.perVert; li_vI++) {
                    lcl_t.mfa_0[li_vI] = lcl_coords.coords.get(li_temp + li_vI);
                }
                
                li_temp = lcla_indexBuffer.get(li_indexBufferIndex + 1) * lcl_coords.perVert;
                for(int li_vI = 0; li_vI < lcl_coords.perVert; li_vI++) {
                    lcl_t.mfa_1[li_vI] = lcl_coords.coords.get(li_temp + li_vI);
                }
                
                li_temp = lcla_indexBuffer.get(li_indexBufferIndex + 2) * lcl_coords.perVert;
                for(int li_vI = 0; li_vI < lcl_coords.perVert; li_vI++) {
                    lcl_t.mfa_2[li_vI] = lcl_coords.coords.get(li_temp + li_vI);
                }
                
                mcla_tris.add(lcl_t);
            }
            
            repaint();
            
        }
        
        @Override
        public Dimension getMinimumSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics cl_g) {
            Graphics2D      lcl_g = (Graphics2D)cl_g.create();
            if((mcl_curDim.width != getWidth()) || (mcl_curDim.getHeight() != getHeight())) {
                build2DTris();
            }
            
            lcl_g.setColor(Color.white);
            DrawUtils.fillComponentRect(lcl_g, this);
            
            if(mcl_image != null) {
                if(!mb_coordsOnly) {
                    lcl_g.drawImage(mcl_image, 0, 0, getWidth(), getHeight(), this);   
                }
            }
            
            lcl_g.setColor(Color.blue);
            
            for(Polygon lcl_t : mcla_2dTris) {
                lcl_g.draw(lcl_t);
            }
            
            if(mi_curTriSel != -1) {
                lcl_g.setColor(Color.red);
                try {
                    Polygon       lcl_t = mcla_2dTris.get(mi_curTriSel);
                    lcl_g.draw(lcl_t);
                } catch (Exception e) {
                    
                }
            }
            
            lcl_g.dispose();
        }
        
        private void    drawTempTri(Graphics cl_g, float f_x1, float f_y1, float f_x2, float f_y2) {
            float   lf_w = (float)(getWidth() - 1);
            float   lf_h = (float)(getHeight() - 1);
            int     li_x1 = (int)(lf_w * f_x1);
            int     li_y1 = (int)(lf_h * f_y1);
            int     li_x2 = (int)(lf_w * f_x2);
            int     li_y2 = (int)(lf_h * f_y2);
            
            cl_g.drawLine(li_x1, li_y1, li_x2, li_y2);
        }
        
    }
    
    class   TempTri {
        float       mfa_0[];
        float       mfa_1[];
        float       mfa_2[];
        
        public  TempTri(int i_per) {
            mfa_0 = new float[i_per];
            mfa_1 = new float[i_per];
            mfa_2 = new float[i_per];
        }
    }
    
    class   Tri2D {
        Point       mcl_pt0 = new Point();
        Point       mcl_pt1 = new Point();
        Point       mcl_pt2 = new Point();
    }
}
